book/[slug]/page.tsx - 书籍章节页

book/[slug]/page.tsx - 书籍章节页

基本信息

属性
路径src/app/book/[slug]/page.tsx
类型Next.js 动态路由页面 (Server Component)
功能显示书籍的单个章节内容

功能描述

渲染书籍章节的 MDX 内容,提供章节导航、翻译支持和 SEO 优化。支持多语言内容回退(优先显示翻译内容,不存在则回退到英文)。

导入依赖

import { notFound } from "next/navigation";
import Link from "next/link";
import { getChapterBySlug, getAdjacentChapters, getAllChapters } from "@/lib/book/chapters";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { MobileTOCButton } from "@/components/book/sidebar";
import type { Metadata } from "next";
import { getTranslations, getLocale } from "next-intl/server";

Props 接口

interface ChapterPageProps {
  params: Promise<{ slug: string }>;
}

静态参数生成

generateStaticParams()

在构建时生成所有章节的静态路径。

export async function generateStaticParams() {
  return getAllChapters().map((chapter) => ({
    slug: chapter.slug,
  }));
}

元数据生成

generateMetadata({ params })

{
  title: `${chapter.title} | The Interactive Book of Prompting`,
  description: `${chapter.description}. Learn ${chapter.title.toLowerCase()} techniques...`,
  keywords: [chapter.title.toLowerCase(), "prompt engineering", ...],
  openGraph: {
    type: "article",
    url: `https://prompts.chat/book/${slug}`
  }
}

章节数据结构

interface Chapter {
  slug: string;
  title: string;
  part: string;      // 所属部分名称
  description?: string;
  order: number;
}

翻译处理

翻译键值

类型键值格式
章节标题chapters.${slug}
章节描述chapterDescriptions.${slug}
部分名称parts.${partKey}

部分名称映射

const partKeys: Record<string, string> = {
  "Introduction": "introduction",
  "Foundations": "foundations",
  "Techniques": "techniques",
  "Advanced": "advanced",
  "Best Practices": "bestPractices",
  "Use Cases": "useCases",
  "Conclusion": "conclusion",
};

回退逻辑

const getChapterTitle = () => {
  try {
    const translated = t(`chapters.${slug}`);
    return translated !== `chapters.${slug}` ? translated : chapter.title;
  } catch {
    return chapter.title;
  }
};

内容加载

多语言内容回退

let Content;
try {
  if (locale !== "en") {
    try {
      Content = (await import(`@/content/book/${locale}/${slug}.mdx`)).default;
    } catch {
      Content = (await import(`@/content/book/${slug}.mdx`)).default;
    }
  } else {
    Content = (await import(`@/content/book/${slug}.mdx`)).default;
  }
} catch {
  Content = () => <div>{t("chapter.comingSoon")}</div>;
}

加载优先级:

  1. 当前语言内容 ({locale}/{slug}.mdx)
  2. 英文内容 ({slug}.mdx)
  3. 即将推出占位符

UI 结构

Article
├── Chapter Header
│   ├── Part Name (small, primary color)
│   ├── Title + MobileTOCButton
│   └── Description (optional)
├── Chapter Content (prose)
│   └── MDX Content
└── Navigation
    ├── Previous Chapter (if exists)
    └── Next Chapter (if exists)

章节导航

相邻章节获取

const { prev, next } = getAdjacentChapters(slug);

导航按钮

位置显示内容
Desktop完整章节标题(翻译后)
Mobile"上一章" / "下一章"
<span className="hidden sm:inline">{translatedTitle}</span>
<span className="sm:hidden">{t("chapter.previous")}</span>

翻译键值

键值用途
book.chapter.notFound章节不存在
book.chapter.comingSoon内容即将推出
book.chapter.previous上一章(移动端)
book.chapter.next下一章(移动端)
chapters.${slug}章节标题翻译
chapterDescriptions.${slug}章节描述翻译
parts.${key}部分名称翻译

组件引用

组件来源用途
MobileTOCButton@/components/book/sidebar移动端目录按钮

内容目录结构

src/content/book/
├── en/
│   ├── 00a-preface.mdx
│   ├── 01-understanding-ai-models.mdx
│   └── ...
├── zh/
│   ├── 00a-preface.mdx
│   └── ...
└── ... (other locales)

路由

路径说明
/book书籍首页
/book/{slug}章节页面(当前)

工具函数

函数来源用途
getChapterBySlug@/lib/book/chapters获取章节信息
getAdjacentChapters@/lib/book/chapters获取相邻章节
getAllChapters@/lib/book/chapters获取所有章节
← 返回目录